package MVC.Models;

import MVC.Model;
import Resources.Enums.Cor;
import Resources.Enums.Quadrante;
import Resources.Exceptions.*;

public class Tabuleiro extends Model {

    /* Attributos */

    private Posicao[][] posicoes;
    private Peca[] pecas;

    private Jogador jogador;
    private Jogador oponente;

    private Boolean emAndamento;
    private Boolean conectado;

    public Tabuleiro(Jogador jogador) {
        this.jogador = jogador;
        emAndamento = false;
        conectado = false;

        posicoes = new Posicao[6][6];
        for (int i = 0; i < 6; i++)
            for (int j = 0; j < 6; j++)
                posicoes[i][j] = new Posicao(this);

        pecas = new Peca[36];

        for (int i = 0; i < 18; i++)
            pecas[i] = new Peca(Cor.PRETO);
        for (int i = 18; i < 36; i++)
            pecas[i] = new Peca(Cor.BRANCO);
    }


    /* Métodos */
    public Peca pegarPeca(Cor cor) {
        Peca novaPecaNoTabuleiro = null;

        for (Peca peca : pecas)
            if (peca.getPosicao() == null && peca.getCor() == cor) {
                novaPecaNoTabuleiro = peca;
                break;
            }

        return novaPecaNoTabuleiro;
    }

    public void esvaziar() {
        for (int i = 0; i < 6; i++)
            for (int j = 0; j < 6; j++)
                posicoes[i][j].setPeca(null);

        emAndamento = false;
    }

    public void prepararJogador(Integer id, Boolean isOponente) {
        Jogador jogadorNaEspera = (isOponente) ? oponente : jogador;

        jogadorNaEspera.setId(id);
        jogadorNaEspera.setCor(Cor.values()[id-1]);
    }

    public void sortear() {
        if(jogador.getId() == 2) {
            jogador.jogar();
            oponente.passar();
        } else {
            oponente.jogar();
            jogador.passar();
        }
    }

    public void proximoJogador() {
        if (jogador.getDaVez()) {
            oponente.jogar();
            jogador.passar();
        } else {
            jogador.jogar();
            oponente.passar();
        }
    }

    public void click(Integer linha, Integer coluna) throws NaoEmAndamentoException, NaoDaVezException, JaPosicionouException, PosicaoOcupadaException {
        Posicao posicao = posicoes[linha][coluna];

        if (!emAndamento)
            throw new NaoEmAndamentoException();
        if (!jogador.getDaVez())
            throw new NaoDaVezException();
        if (jogador.getPosicionou())
            throw new JaPosicionouException();
        if (posicao.getPeca() != null)
            throw new PosicaoOcupadaException();
    }

    public void inserirPeca(Integer linha, Integer coluna) {
        Posicao posicao = posicoes[linha][coluna];

        if (jogador.getDaVez()) {
            Peca peca = pegarPeca(jogador.getCor());
            posicao.setPeca(peca);
            jogador.setPosicionou(true);
        } else {
            Peca peca = pegarPeca(oponente.getCor());
            posicao.setPeca(peca);
            oponente.setPosicionou(true);
        }

    }

    public void clickRotacao(Quadrante quadrante, Boolean horario) throws NaoEmAndamentoException, NaoDaVezException, NaoPosicionouException {
        if (!emAndamento)
            throw new NaoEmAndamentoException();
        if (!jogador.getDaVez())
            throw new NaoDaVezException();
        if (!jogador.getPosicionou())
            throw new NaoPosicionouException();
    }

    public void realizarRotacao(Quadrante quadrante, Boolean horario) {
        Posicao[][] quadranteReal = this.getQuadrante(quadrante);

        if(horario)
            this.turnRight(quadranteReal);
        else
            this.turnLeft(quadranteReal);
    }

    public Cor tratarLance() {
        Cor cor = percorrerHorizontal();

        if (cor == null) {
            cor = percorrerVertical();
            if (cor == null) {
                cor = percorrerDiagonalDireitaEsquerda();
                if (cor == null) {
                    cor = percorrerDiagonalEsquerdaDireita();
                }
            }
        }

        return cor;
    }

    public Boolean tratarTabuleiroCheio() {
        for (int i = 0; i < 6; i++)
            for (int j = 0; j < 6; j++)
                if (posicoes[i][j].getPeca() == null)
                    return false;

        return true;
    }

    public void definirVencedor(Cor cor) {
        if (jogador.getCor() == cor) {
            jogador.vencer();
            oponente.perder();
        } else {
            oponente.vencer();
            jogador.perder();
        }

        emAndamento = false;
    }

    public void definirEmpate() {
        jogador.perder();
        oponente.perder();

        emAndamento = false;
    }

    private Cor percorrerHorizontal() {
        Cor cor = null;

        /* horizontal */
        for (int i = 0; i < 6; i++) {
            Posicao[] linha = new Posicao[6];
            for (int j = 0; j < 6; j++)
                linha[j] = posicoes[i][j];

            cor = acharSequencia(linha);
            if (cor != null)
                break;
        }

        return cor;
    }

    private Cor percorrerVertical() {
        Cor cor = null;

        /* vertical */
        for (int i = 0; i < 6; i++) {
            Posicao[] coluna = new Posicao[6];
            for (int j = 0; j < 6; j++)
                coluna[j] = posicoes[j][i];

            cor = acharSequencia(coluna);
            if (cor != null)
                break;
        }

        return cor;
    }

    private Cor percorrerDiagonalEsquerdaDireita() {
        Cor cor = null;

        /* diagonal principal esquerda-direita */
        Posicao[] diagonalPrincipal = new Posicao[6];
        for (int i = 0; i < 6; i++)
            diagonalPrincipal[i] = posicoes[i][i];
        cor = acharSequencia(diagonalPrincipal);
        if (cor != null)
            return cor;

        /* diagonal auxiliares esquerda-direita */
        Posicao[] diagonalAuxiliar1 = new Posicao[5];
        for (int i = 0; i < 5; i++)
            diagonalAuxiliar1[i] = posicoes[i][i+1];
        cor = acharSequencia(diagonalAuxiliar1);
        if (cor != null)
            return cor;

        Posicao[] diagonalAuxiliar2 = new Posicao[5];
        for (int i = 0; i < 5; i++)
            diagonalAuxiliar2[i] = posicoes[i+1][i];
        cor = acharSequencia(diagonalAuxiliar2);
        if (cor != null)
            return cor;

        return cor;
    }

    private Cor percorrerDiagonalDireitaEsquerda() {
        Cor cor = null;

        /* diagonal principal direita-esquerda */
        Posicao[] diagonalPrincipal = new Posicao[6];
        for (int i = 0; i < 6; i++)
            diagonalPrincipal[i] = posicoes[i][5-i];
        cor = acharSequencia(diagonalPrincipal);
        if (cor != null)
            return cor;

        /* diagonal auxiliares direita-esquerda */
        Posicao[] diagonalAuxiliar1 = new Posicao[5];
        for (int i = 0; i < 5; i++)
            diagonalAuxiliar1[i] = posicoes[i+1][5-i];
        cor = acharSequencia(diagonalAuxiliar1);
        if (cor != null)
            return cor;

        Posicao[] diagonalAuxiliar2 = new Posicao[5];
        for (int i = 0; i < 5; i++)
            diagonalAuxiliar2[i] = posicoes[i][5-(i+1)];
        cor = acharSequencia(diagonalAuxiliar2);
        if (cor != null)
            return cor;

        return cor;
    }

    private Cor acharSequencia(Posicao[] posicoes) {
        Integer sequencia = 0;
        Cor cor = null;

        for(Posicao posicao : posicoes) {
            Peca peca = posicao.getPeca();

            if (peca != null) {
                if (cor == null)
                    cor = peca.getCor();

                if (cor == peca.getCor())
                    sequencia++;
                else {
                    sequencia = 1;
                    cor = peca.getCor();
                }
            } else
                sequencia = 0;

            if (sequencia == 5)
                return cor;
        }

        return null;
    }

    private Posicao[][] getQuadrante(Quadrante quad) {
        Posicao[][] retorno = new Posicao[3][3];
        switch(quad){
            case PRIMEIRO:
                for (int i = 0; i < 3; i++)
                    for (int j = 0; j < 3; j++)
                        retorno[i][j] = this.posicoes[i][j];
                break;
            case SEGUNDO:
                for (int i = 0; i < 3; i++)
                    for (int j = 0; j < 3; j++)
                        retorno[i][j] = this.posicoes[i][j+3];
                break;
            case TERCEIRO:
                for (int i = 0; i < 3; i++)
                    for (int j = 0; j < 3; j++)
                        retorno[i][j] = this.posicoes[i+3][j+3];
                break;
            case QUARTO:
                for (int i = 0; i < 3; i++)
                    for (int j = 0; j < 3; j++)
                        retorno[i][j] = this.posicoes[i+3][j];
                break;
        }

        return retorno;
    }

    private void turnRight(Posicao[][] quadranteReal){
        Posicao[][] oClone = this.cloneQuadrante(quadranteReal);

        quadranteReal[0][0].setPeca(oClone[2][0].getPeca());
        quadranteReal[0][1].setPeca(oClone[1][0].getPeca());
        quadranteReal[0][2].setPeca(oClone[0][0].getPeca());
        quadranteReal[1][0].setPeca(oClone[2][1].getPeca());
        quadranteReal[1][2].setPeca(oClone[0][1].getPeca());
        quadranteReal[2][0].setPeca(oClone[2][2].getPeca());
        quadranteReal[2][1].setPeca(oClone[1][2].getPeca());
        quadranteReal[2][2].setPeca(oClone[0][2].getPeca());
    }

    private void turnLeft(Posicao[][] quadranteReal){
        Posicao[][] oClone = this.cloneQuadrante(quadranteReal);

        quadranteReal[0][0].setPeca(oClone[0][2].getPeca());
        quadranteReal[0][1].setPeca(oClone[1][2].getPeca());
        quadranteReal[0][2].setPeca(oClone[2][2].getPeca());
        quadranteReal[1][0].setPeca(oClone[0][1].getPeca());
        quadranteReal[1][2].setPeca(oClone[2][1].getPeca());
        quadranteReal[2][0].setPeca(oClone[0][0].getPeca());
        quadranteReal[2][1].setPeca(oClone[1][0].getPeca());
        quadranteReal[2][2].setPeca(oClone[2][0].getPeca());
    }

    private Posicao[][] cloneQuadrante(Posicao[][] quadranteReal){
        Posicao[][] pos = new Posicao[quadranteReal.length][quadranteReal.length];
        for (int i = 0; i < quadranteReal.length; i++)
            for (int j = 0; j < quadranteReal.length; j++){
                pos[i][j] = new Posicao(this);
                pos[i][j].setPeca(quadranteReal[i][j].getPeca());
            }

        return pos;
    }

    /* Getters e Setters */

    public Posicao[][] getPosicoes() {
        return posicoes;
    }

    public void setPosicoes(Posicao[][] posicoes) {
        this.posicoes = posicoes;
    }

    public Peca[] getPecas() {
        return pecas;
    }

    public void setPecas(Peca[] pecas) {
        this.pecas = pecas;
    }

    public Jogador getJogador() {
        return jogador;
    }

    public void setJogador(Jogador jogador) {
        this.jogador = jogador;
    }

    public Jogador getOponente() {
        return oponente;
    }

    public void setOponente(Jogador oponente) {
        this.oponente = oponente;
    }

    public Boolean getEmAndamento() {
        return emAndamento;
    }

    public void setEmAndamento(Boolean emAndamento) {
        this.emAndamento = emAndamento;
    }

    public Boolean getConectado() {
        return conectado;
    }

    public void setConectado(Boolean conectado) {
        this.conectado = conectado;
    }
}
